#region File Description
//-----------------------------------------------------------------------------
// FightingCharacter.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
#endregion

namespace MilwormsGameData
{
    /// <summary>
    /// A character that engages in combat.
    /// </summary>
    public abstract class FightingCharacter : Character
    {
        #region Class Data


        /// <summary>
        /// The name of the character class.
        /// </summary>
        private string characterClassContentName;

        /// <summary>
        /// The name of the character class.
        /// </summary>
        public string CharacterClassContentName
        {
            get { return characterClassContentName; }
            set { characterClassContentName = value; }
        }


        /// <summary>
        /// The character class itself.
        /// </summary>
        private CharacterClass characterClass;

        /// <summary>
        /// The character class itself.
        /// </summary>
        [ContentSerializerIgnore]
        public CharacterClass CharacterClass
        {
            get { return characterClass; }
            set
            {
                characterClass = value;
            }
        }


        /// <summary>
        /// The level of the character.
        /// </summary>
        private int characterLevel = 1;

        /// <summary>
        /// The level of the character.
        /// </summary>
        public int CharacterLevel
        {
            get { return characterLevel; }
            set
            {
                characterLevel = value;
            }
        }


        /// <summary>
        /// Returns true if the character is at the maximum level allowed by their class.
        /// </summary>
        public bool IsMaximumCharacterLevel
        {
            get
            {
                return true; // characterLevel >= characterClass.LevelEntries.Count;
            }
        }

        #endregion

        #region Experience


        /// <summary>
        /// The amount of experience points that this character has.
        /// </summary>
        private int experience;

        /// <summary>
        /// The amount of experience points that this character has.
        /// </summary>
        [ContentSerializerIgnore]
        public int Experience
        {
            get { return experience; }
            set
            {
                experience = value;
                while (experience >= ExperienceForNextLevel)
                {
                    if (IsMaximumCharacterLevel)
                    {
                        break;
                    }
                    experience -= ExperienceForNextLevel;
                    CharacterLevel++;
                }
            }
        }


        /// <summary>
        /// Returns the amount of experience necessary to reach the next character level.
        /// </summary>
        public int ExperienceForNextLevel
        {
            get
            {
                int checkIndex = Math.Min(characterLevel,
                    characterClass.LevelEntries.Count) - 1;
                return characterClass.LevelEntries[checkIndex].ExperiencePoints;
            }
        }


        #endregion

        #region Equipment


        /// <summary>
        /// The equipment currently equipped on this character.
        /// </summary>
        private List<Equipment> equippedEquipment = new List<Equipment>();

        /// <summary>
        /// The equipment currently equipped on this character.
        /// </summary>
        [ContentSerializerIgnore]
        public List<Equipment> EquippedEquipment
        {
            get { return equippedEquipment; }
        }


        /// <summary>
        /// The content names of the equipment initially equipped on the character.
        /// </summary>
        private List<string> initialEquipmentContentNames = new List<string>();

        /// <summary>
        /// The content names of the equipment initially equipped on the character.
        /// </summary>
        public List<string> InitialEquipmentContentNames
        {
            get { return initialEquipmentContentNames; }
            set { initialEquipmentContentNames = value; }
        }

        /// <summary>
        /// Equip a new piece of equipment.
        /// </summary>
        /// <returns>True if the equipment could be equipped.</returns>
        public virtual bool Equip(Equipment equipment)
        {
            Equipment oldEquipment;

            return Equip(equipment, out oldEquipment);
        }


        /// <summary>
        /// Equip a new piece of equipment, specifying any equipment auto-unequipped.
        /// </summary>
        /// <returns>True if the equipment could be equipped.</returns>
        public virtual bool Equip(Equipment equipment, out Equipment oldEquipment)
        {
            if (equipment == null)
            {
                throw new ArgumentNullException("equipment");
            }

            else
            {
                oldEquipment = null;
            }

            return false;
        }


        /// <summary>
        /// Unequip a piece of equipment.
        /// </summary>
        /// <returns>True if the equipment could be unequipped.</returns>
        public virtual bool Unequip(Equipment equipment)
        {
            if (equipment == null)
            {
                throw new ArgumentNullException("equipment");
            }

            if (equippedEquipment.Remove(equipment))
            {
                return true;
            }

            return false;
        }


        #endregion


        #region Inventory


        /// <summary>
        /// The gear in this character's inventory (and not equipped).
        /// </summary>
        private List<ContentEntry<Gear>> inventory = new List<ContentEntry<Gear>>();

        /// <summary>
        /// The gear in this character's inventory (and not equipped).
        /// </summary>
        public List<ContentEntry<Gear>> Inventory
        {
            get { return inventory; }
        }


        #endregion


        #region Content Type Reader


        /// <summary>
        /// Reads a FightingCharacter object from the content pipeline.
        /// </summary>
        public class FightingCharacterReader : ContentTypeReader<FightingCharacter>
        {
            /// <summary>
            /// Reads a FightingCharacter object from the content pipeline.
            /// </summary>
            protected override FightingCharacter Read(ContentReader input,
                FightingCharacter existingInstance)
            {
                FightingCharacter fightingCharacter = existingInstance;
                if (fightingCharacter == null)
                {
                    throw new ArgumentNullException("existingInstance");
                }

                input.ReadRawObject<Character>(fightingCharacter as Character);

                fightingCharacter.CharacterClassContentName = input.ReadString();
                fightingCharacter.CharacterLevel = input.ReadInt32();
                fightingCharacter.InitialEquipmentContentNames.AddRange(
                    input.ReadObject<List<string>>());
                fightingCharacter.Inventory.AddRange(
                    input.ReadObject<List<ContentEntry<Gear>>>());

                // load the character class
                fightingCharacter.CharacterClass =
                    input.ContentManager.Load<CharacterClass>(
                        System.IO.Path.Combine("CharacterClasses",
                            fightingCharacter.CharacterClassContentName));

                // populate the equipment list
                foreach (string gearName in
                    fightingCharacter.InitialEquipmentContentNames)
                {
                    fightingCharacter.Equip(input.ContentManager.Load<Equipment>(
                        System.IO.Path.Combine("Gear", gearName)));
                }

                // populate the inventory based on the content names
                foreach (ContentEntry<Gear> inventoryEntry in
                    fightingCharacter.Inventory)
                {
                    inventoryEntry.Content = input.ContentManager.Load<Gear>(
                        System.IO.Path.Combine("Gear", inventoryEntry.ContentName));
                }

                return fightingCharacter;
            }
        }


        #endregion
    }
}
